#include "preHeader.h"
#include "SocialServerSession.h"
#include "SocialServerSessionHandler.h"
#include "ServerMaster.h"
#include "GameServer.h"
#include "network/ConnectionManager.h"
#include "MapServerMgr.h"
#include "GateServerMgr.h"
#include "CenterSession.h"
#include "Game/AccountMgr.h"
#include "Game/CharacterMgr.h"
#include "Manager/DataMgr.h"
#include "Manager/GuildLeagueMgr.h"
#include "Mail/MailMgr.h"

SocialServerSession::SocialServerSession()
: RPCSession("SocialServerSession", SGX_RPC_INVOKE_RESP)
{
}

SocialServerSession::~SocialServerSession()
{
}

void SocialServerSession::CheckConnection()
{
	const auto& connPrePtr = GetConnection();
	if (connPrePtr && connPrePtr->IsActive()) {
		return;
	}
	if (!IsStatus(Idle)) {
		return;
	}

	auto& cfg = IServerMaster::GetInstance().GetConfig();
	auto connPtr = sConnectionManager.NewConnection(*this);
	connPtr->AsyncConnect(
		cfg.GetString("SOCIAL_SERVER", "HOST", "127.0.0.1"),
		cfg.GetString("SOCIAL_SERVER", "PORT", "9996"));

	sSessionManager.AddSession(this);
}

int SocialServerSession::HandlePacket(INetPacket *pck)
{
	return sSocialServerSessionHandler.HandlePacket(this, *pck);
}

void SocialServerSession::OnConnected()
{
	NetPacket req(CGX_REGISTER);
	req << sCenterSession.GetServerId()
		<< (s64)sDataMgr.getStartServerTime()
		<< (s64)sDataMgr.getLastOperatingEventTime();
	PushSendPacket(req);
	RPCSession::OnConnected();
}

void SocialServerSession::OnShutdownSession()
{
	WLOG("Close SocialServerSession.");
	Session::OnShutdownSession();
}

void SocialServerSession::DeleteObject()
{
	ClearRecvPacket();
	ClearShutdownFlag();
	SetStatus(Idle);
}

void SocialServerSession::PushServerId(uint32 serverId)
{
	NetPacket pack(CGX_PUSH_SERVER_ID);
	pack << serverId;
	PushSendPacket(pack);
}

void SocialServerSession::RPCReplyError(uint64 sn, GErrorCode errCode)
{
	NetPacket rpcRespPck(CGX_RPC_INVOKE_RESP);
	rpcRespPck << (int32)errCode;
	RPCReply(rpcRespPck, sn);
}

void SocialServerSession::BroadcastPacket2AllServices(const INetPacket& pck)
{
	NetPacket wrapper(CGX_BROADCAST_SERVICES);
	PushSendPacket(wrapper, pck);
}

void SocialServerSession::RestoreCharacters()
{
	size_t i = 0;
	NetPacket pack(CGX_RESTORE_CHARACTERS);
	auto& allOnlineCharacters = sCharacterMgr.GetOnlineCharacters();
	for (auto&[guid, pChar] : allOnlineCharacters) {
		pack << guid.UID
			<< pChar->GetAccount()->GetSession()->sn()
			<< pChar->GetAccount()->sn;
		if (++i > 3000) {
			PushSendPacket(pack);
			pack.Clear();
			i = 0;
		}
	}
	if (i > 0) {
		PushSendPacket(pack);
	}
}

void SocialServerSession::PushCharacterOnline(Character* pChar)
{
	NetPacket pack(CGX_CHARACTER_ONLINE);
	pack << pChar->guid.UID
		<< pChar->GetAccount()->GetSession()->sn()
		<< pChar->GetAccount()->sn;
	PushSendPacket(pack);
}

void SocialServerSession::PushCharacterOffline(Character* pChar)
{
	NetPacket pack(CGX_CHARACTER_OFFLINE);
	pack << pChar->guid.UID;
	PushSendPacket(pack);
}

void SocialServerSession::PullAllGuilds()
{
	NetPacket pack(CGX_GET_ALL_GUILDS);
	PushSendPacket(pack);
}

void SocialServerSession::PullAllCharacterGuilds()
{
	for (auto&[guid, pChar] : sCharacterMgr.GetCharacterInfoMap()) {
		if (pChar->guildId != 0) {
			OnCharacterQuitGuild(pChar, GUILD_QUIT_CAUSE::NET,
				pChar->guildId, pChar->guildName);
		}
	}
	NetPacket pack(CGX_GET_ALL_CHARACTER_GUILDS);
	PushSendPacket(pack);
}

void SocialServerSession::OnCharacterJoinGuild(Character* pChar, GUILD_JOIN_CAUSE cause,
	uint32 guildId, int8 guildTitle, const std::string_view& guildName)
{
	DBGASSERT(pChar->guildId == 0 || pChar->guildId == guildId);
	pChar->guildId = guildId;
	pChar->guildTitle = guildTitle;
	pChar->guildName = guildName;
	if (cause == GUILD_JOIN_CAUSE::NET) {
		return;
	}

	if (pChar->isOnline) {
		NetPacket notify(G2M_JOIN_GUILD_EVENT);
		notify << guildId << guildTitle << guildName;
		sMapServerMgr.RouteToPlayer(pChar, notify);
	}
}

void SocialServerSession::OnCharacterQuitGuild(Character* pChar, GUILD_QUIT_CAUSE cause,
	uint32 guildId, const std::string_view& guildName)
{
	DBGASSERT(pChar->guildId == 0 || pChar->guildId == guildId);
	pChar->guildId = 0;
	pChar->guildTitle = 0;
	pChar->guildName.clear();
	if (cause == GUILD_QUIT_CAUSE::NET) {
		return;
	}

	if (pChar->isOnline) {
		NetPacket notify(G2M_QUIT_GUILD_EVENT);
		notify << guildId << guildName;
		sMapServerMgr.RouteToPlayer(pChar, notify);
	}
}

int SocialServerSessionHandler::HandleRegisterResp(SocialServerSession *pSession, INetPacket &pck)
{
	pck >> pSession->m_socialListenAddr >> pSession->m_socialListenPort;
	sGateServerMgr.BroadcastSocialListen2AllGateServer(
		pSession->m_socialListenAddr, pSession->m_socialListenPort);

	pSession->OnRPCSessionReady();
	pSession->RestoreCharacters();
	pSession->PullAllCharacterGuilds();
	pSession->PullAllGuilds();
	sCharacterMgr.PushAllCharacterRankDatas();

	NLOG("Register to SocialServer Success.");
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleToAllMapServerPacket(SocialServerSession *pSession, INetPacket &pck)
{
	sMapServerMgr.BroadcastPacket2AllMapServer(pck.UnpackPacket());
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleToMapServerInstancePacket(SocialServerSession *pSession, INetPacket &pck)
{
	InstGUID instGuid;
	pck >> instGuid;
	sMapServerMgr.SendPacket2MapServer(instGuid, pck.UnpackPacket());
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleToMapServerPlayerPacket(SocialServerSession *pSession, INetPacket &pck)
{
	uint32 uid;
	pck >> uid;
	Character* pChar = sCharacterMgr.GetCharacter(uid);
	if (pChar != NULL) {
		sMapServerMgr.SendPacket2MapServer(pChar, pck.UnpackPacket());
	}
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleToInstancePacket(SocialServerSession *pSession, INetPacket &pck)
{
	InstGUID instGuid;
	pck >> instGuid;
	sMapServerMgr.RouteToInstance(instGuid, pck.UnpackPacket());
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleToPlayerPacket(SocialServerSession *pSession, INetPacket &pck)
{
	uint32 uid;
	pck >> uid;
	Character* pChar = sCharacterMgr.GetCharacter(uid);
	if (pChar != NULL) {
		sMapServerMgr.RouteToPlayer(pChar, pck.UnpackPacket());
	}
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleKick(SocialServerSession *pSession, INetPacket &pck)
{
	uint32 uid;
	int32 errCode;
	pck >> uid >> errCode;

	Character* pChar = sCharacterMgr.GetCharacter(uid);
	if (pChar != NULL) {
		sAccountMgr.KickAccount(pChar->acct, (GErrorCode)errCode);
	}

	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandlePullCharacterInfos(SocialServerSession *pSession, INetPacket &pck, const RPCActor::RequestMetaInfo &info)
{
	uint32 flags, types;
	pck >> flags >> types;
	NetPacket rpcRespPck(SGX_RPC_INVOKE_RESP);
	size_t anchor = 0, num = 0;
	if ((flags & (u32)PullValueFlag::NonDict) == 0) {
		anchor = rpcRespPck.Placeholder<u16>(0);
	}
	while (pck.IsReadableEmpty()) {
		uint32 infoKey, playerId;
		if ((flags & (u32)PullValueFlag::ExtraKey) == 0) {
			pck >> playerId; infoKey = playerId;
		} else {
			pck >> infoKey >> playerId;
		}
		auto pChar = sCharacterMgr.GetCharacter(playerId);
		if (pChar == NULL) {
			continue;
		}
		NetBuffer buffer;
		if ((types & (u32)CharValueType::Key) != 0) {
			buffer << playerId;
		}
		if ((types & (u32)CharValueType::Name) != 0) {
			buffer << pChar->name;
		}
		if ((types & (u32)CharValueType::Level) != 0) {
			buffer << pChar->lastLevel;
		}
		if ((types & (u32)CharValueType::FightValue) != 0) {
			buffer << pChar->gsReadVals.lastFightValue;
		}
		if ((types & (u32)CharValueType::LastOnlineTime) != 0) {
			buffer << s64(!pChar->isOnline ? pChar->lastOnlineTime : ~0);
		}
		if ((flags & (u32)PullValueFlag::NonDict) == 0) {
			rpcRespPck << infoKey << buffer.CastBufferStringView();
		} else {
			rpcRespPck.Append(buffer.GetBuffer(), buffer.GetTotalSize());
		}
		++num;
	}
	if ((flags & (u32)PullValueFlag::NonDict) == 0) {
		rpcRespPck.Put(anchor, (u16)num);
	}
	pSession->RPCReply(rpcRespPck, info.sn);
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandlePushAllGuilds(SocialServerSession *pSession, INetPacket &pck)
{
	std::unordered_set<uint32> guildIds;
	while (!pck.IsReadableEmpty()) {
		guildIds.insert(pck.Read<uint32>());
	}
	sGuildLeagueMgr.CleanInvalidGuilds(guildIds);
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandlePushCharacterGuilds(SocialServerSession *pSession, INetPacket &pck)
{
	while (!pck.IsReadableEmpty()) {
		uint32 playerId, guildId;
		int8 guildTitle;
		std::string_view guildName;
		pck >> playerId >> guildId >> guildTitle >> guildName;
		auto pChar = sCharacterMgr.GetCharacter(playerId);
		if (pChar != NULL) {
			pSession->OnCharacterJoinGuild(pChar, GUILD_JOIN_CAUSE::NET,
				guildId, guildTitle, guildName);
		}
	}
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleCharacterJoinGuild(SocialServerSession *pSession, INetPacket &pck)
{
	uint32 playerId, guildId;
	int8 guildTitle, cause;
	std::string_view guildName;
	pck >> playerId >> guildId >> guildTitle >> guildName >> cause;
	auto pChar = sCharacterMgr.GetCharacter(playerId);
	if (pChar != NULL) {
		pSession->OnCharacterJoinGuild(pChar, (GUILD_JOIN_CAUSE)cause,
			guildId, guildTitle, guildName);
	}
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleCharacterQuitGuild(SocialServerSession *pSession, INetPacket &pck)
{
	uint32 playerId, guildId;
	std::string_view guildName;
	int8 cause;
	pck >> playerId >> guildId >> guildName >> cause;
	auto pChar = sCharacterMgr.GetCharacter(playerId);
	if (pChar != NULL) {
		pSession->OnCharacterQuitGuild(pChar, (GUILD_QUIT_CAUSE)cause,
			guildId, guildName);
	}
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleSaveLastOperatingEventTime(SocialServerSession *pSession, INetPacket &pck)
{
	int64 lastOperatingEventTime;
	pck >> lastOperatingEventTime;
	sDataMgr.saveLastOperatingEventTime(lastOperatingEventTime);
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleSendOperatingRewardMail(SocialServerSession* pSession, INetPacket& pck)
{
	uint32 playerId, subjectStrId, bodyStrId, times;
	std::string_view subjectArgs, bodyArgs;
	pck >> playerId >> subjectStrId >> bodyStrId
		>> subjectArgs >> bodyArgs >> times;

	TextPacker subject, body;
	(subject << subjectStrId).Append(subjectArgs);
	(body << bodyStrId).Append(bodyArgs);
	auto mailProp = NewSystemMailInstance(
		playerId, MailFlag_Args, subject.str(), body.str());

	auto n = pck.Read<uint16>();
	for (uint16 i = 0; i < n; ++i) {
		FItemInfo rewardItem;
		LoadFromINetStream(rewardItem, pck);
		if (rewardItem.id < ITEM_CHEQUE_TYPE) {
			AppendCheque2MailInstance(
				mailProp, rewardItem.id, rewardItem.num * times);
		} else {
			AppendItem2MailInstance(mailProp, NewItemProp4Flags(
				rewardItem.id, rewardItem.num * times, rewardItem.flags).Save());
		}
	}

	sMailMgr.SendMail(mailProp);
	return SessionHandleSuccess;
}

int SocialServerSessionHandler::HandleSyncOperatingCares(SocialServerSession *pSession, INetPacket &pck)
{
	std::string operatingCares;
	pck >> operatingCares;
	sDataMgr.setOperatingCares(operatingCares);
	NetPacket pack(GS_SYNC_OPERATING_CARES);
	pack << operatingCares;
	sMapServerMgr.BroadcastPacket2AllMapServer(pack);
	return SessionHandleSuccess;
}
